/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

static void print_job_output(char *, struct QuickPoolJobInfo *, bool_t, char **);
static void output(char *, char *, bool_t, char *, char **);
static void remoteOutput(int, char **, char *, char *, char *, char **);
static int useTmp(char *, char *);
static void stripClusterName(char *);
static void peekjob_usage(const char *);

#define MAX_PEEK_ARGS  5

int
cmd_peek_job(int argc, char **argv)
{
    fprintf(stderr, "Not support in quickpool by far.\n");
    return (-1);

    bool_t fOpt=FALSE;
    while (TRUE) {
        int option_index = 0;
        static struct option long_options[] = {
            {"help", no_argument, 0, 'h'},
            {"fork", no_argument, 0, 'f'},
            {NULL, 0, 0, 0}
        };

        int opt;
        opt = getopt_long(argc, argv, "hf", long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
        case 'f':
            fOpt = TRUE;
            break;
        case 'h':
            peekjob_usage(argv[0]);
            exit(0);
        case '?':
        default:
            peekjob_usage(argv[0]);
            exit(1);
        }
    }

    if (argc <= optind) {
        fprintf(stderr, "Must specify a job ID.\n");
        peekjob_usage(argv[0]);
        exit(1);
    }

    JOBID_T jobId;
    if (get_one_jobid(argv[argc - 1], &jobId)) {
        exit(2);
    }

    struct request jobRequest;
    memset(&jobRequest, 0, sizeof(struct request));

    jobRequest.number = 1;
    jobRequest.keyvalues = (struct keyvalue *)calloc(jobRequest.number,
                                                     sizeof(struct keyvalue));
    if (NULL == jobRequest.keyvalues) {
        fprintf(stderr, "calloc(%ld) failed due to %m.\n",
                jobRequest.number*sizeof(struct keyvalue));
        exit(4);
    }

    jobRequest.keyvalues[0].key = REQUEST_KEY_JOBIDS;
    jobRequest.keyvalues[0].value = (char *)calloc(32, sizeof(char));
    if (NULL == jobRequest.keyvalues[0].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 32);
        exit(4);
    }
    snprintf(jobRequest.keyvalues[0].value, 32, "%lld", jobId);

    char strJobId[32];
    struct QuickPoolJobInfo *jInfo=NULL;
    struct QuickPoolJobReply *jobReply;
    quickpool_errno = 0;
    jobReply = client_get_jobids(&jobRequest, NULL);
    if (quickpool_errno != 0 || NULL == jobReply) {

        if (NULL == jobReply) {
            fprintf(stderr, "Cannot peek job <%s> output due to above error.\n",
                    jobid_2string(jInfo->jobid, strJobId));
        } else {

            fprintf(stderr, "Cannot peek job <%s> output due to %s.\n",
                    jobid_2string(jInfo->jobid, strJobId), jobReply->error_messages[0]);
            client_finish_getting();
        }

        exit(3);
    }

    gf_xdr_free(libxdr_job_reply, jobReply);
    FREEUP(jobReply);

    jInfo = client_get_jobinfo();
    if (NULL == jInfo) {
        fprintf(stderr, "Cannot get job <%s> due to above error.\n",
                jobid_2string(jInfo->jobid, strJobId));
        exit(3);
    }

    client_finish_getting();

    if (jobId && jInfo->jobid != jobId) {
        quickpool_errno = ERROR_JOB_ARRAY;
        fprintf(stderr, "Cannot peek an array job <%s>.\n", jobid_2string(jInfo->jobid, strJobId));
        exit(3);
    }

    if (IS_JOB_PENDING(jInfo->status) || jInfo->execute_user[0] == '\0') {
        fprintf(stderr, "Job <%s> is not yet started.\n", jobid_2string(jInfo->jobid, strJobId));

        exit(3);
    }

    if (IS_JOB_ENDED(jInfo->status)) {
        fprintf(stderr, "Job <%s> has been done.\n", jobid_2string(jInfo->jobid, strJobId));
        exit(3);
    }

    char *strOutFile;
    strOutFile = client_peekjob(jInfo->jobid);
    if (NULL == strOutFile) {
        exit(3);
    }

    print_job_output(strOutFile, jInfo, fOpt, environ);

    exit(0);

}

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

static void
print_job_output(char *jobFile, struct QuickPoolJobInfo *jInfo, bool_t fopt, char **envp)
{
    char fileOut[PATH_MAX+1];
    char fileErr[PATH_MAX+1];

    snprintf(fileOut, PATH_MAX, "%s.out", jobFile);
    snprintf(fileErr, PATH_MAX, "%s.err", jobFile);
    stripClusterName(jInfo->execute_nodes[0]);

    if (!((NULL != jInfo->submit->output_file)
          && strcmp(jInfo->submit->output_file, "/dev/null") == 0)) {

        fprintf(stdout, "<< %s >>\n","output from stdout");
        output(fileOut, jInfo->execute_nodes[0], fopt, jInfo->execute_user, envp);
    }

    if ((NULL != jInfo->submit->error_file)
        && strcmp(jInfo->submit->error_file, "/dev/null") != 0) {

        fprintf(stdout, "\n<< %s >>\n", "output from stderr");
        output(fileErr, jInfo->execute_nodes[0], fopt, jInfo->execute_user, envp);
    }

    return;
}


static void
output(char *fname, char *exHost, bool_t fopt, char *execUsername, char **envp)
{
    char *disOut[MAX_PEEK_ARGS];
    int fidx;
    int pid=0;
    struct stat buf;

    if (!fopt) {

        pid = fork();
        if (pid < 0) {
            perror("fork");
            exit (-1);
         }
    }

    if ( pid > 0) {

        wait(NULL);
        return;
     }


    if (fopt) {
        disOut[0] = (char *)"tail";
	disOut[1] = (char *)"-f";
	fidx = 2;
    } else {
        disOut[0] = (char *)"cat";
	fidx = 1;
    }
    disOut[fidx+1] = NULL;


    if ( fname[0] != '/' || stat(fname,&buf) < 0) {
	remoteOutput(fidx, disOut, exHost, fname, execUsername, envp);
    } else {

        uid_t uid;
        uid = getuid();
        if (0 > setuid(uid)) {
            perror("setuid");
        }

	disOut[fidx] = fname;
	execvp(disOut[0], disOut);
	fprintf(stderr, STR_FUNC_S_S_FAIL_S, "execvp", disOut[0], strerror(errno));
    }
    exit(-1);

}

static void
remoteOutput(int fidx, char **disOut, char *exHost, char *fname,
	     char *execUsername, char **envp )
{
    char buf[PATH_MAX+1];
    char *args[MAX_PEEK_ARGS+4];
    char *qpUserName;

#define RSHCMD ((char *)"rsh")

    qpUserName = getmy_uname();
    if (strcmp(qpUserName, execUsername)) {

        if (useTmp(exHost, fname)) {
            snprintf(buf, PATH_MAX, "/tmp/.output%d/%s", (int)getuid(), fname);
            disOut[fidx] = buf;
        } else {
            disOut[fidx] = fname;
        }

	args[0] = RSHCMD;
	args[1] = exHost;
	args[2] = (char *)"-l";
	args[3] = execUsername;
	if (fidx == 2) {
	    args[4] = disOut[0];
	    args[5] = disOut[1];
	    args[6] = disOut[2];
	    args[7] = NULL;
	} else {
	    args[4] = disOut[0];
	    args[5] = disOut[1];
	    args[6] = NULL;
	}

        FREEUP(qpUserName);
	execvp(RSHCMD, args);
	fprintf(stderr, STR_FUNC_S_S_FAIL_S, "execvp", args[0], strerror(errno));
	return;
    }
    FREEUP(qpUserName);

    if (useTmp(exHost, fname)) {
        snprintf(buf, PATH_MAX, "/tmp/.output%d/%s", (int) getuid(), fname);
	disOut[fidx] = buf;
    } else {
	disOut[fidx] = fname;
    }

    return;
}

static int
useTmp(char *exHost, char *fname)
{
    pid_t pid;
    pid = fork();
    if (pid == -1) {
	perror ("fork");
	return FALSE;
    }

    if (0 == pid) {

	exit(FALSE);
    }

    STATUS_WAIT_T status;
    if (waitpid(pid, &status, 0) == -1) {
	perror("waitpid");
	return FALSE;
    }

    return (WEXITSTATUS(status));
}

static void
stripClusterName(char *str)
{
    char *p;

    if ((p = strchr(str, '@')) == NULL) {
	return;
    }
    str[p-str] = '\0';
    return;
}

static void
peekjob_usage(const char *cmd)
{
    fprintf(stdout, "qp job %s usage: [-h, --help] [-f, --fork]\n", cmd);
    fprintf(stdout, "\t\t   [-m, --node host_name | -q, --queue queue_name | -J, --jobname job_name | jobId | \"jobId[index]\"]\n");

    return;
}
